package com.spdd.mt.biz.manager.channel;

import cn.hutool.core.date.DateUtil;
import com.spdd.mt.biz.config.ConfigBean;
import com.spdd.mt.biz.config.SpddMapping;
import com.spdd.mt.biz.constant.*;
import com.spdd.mt.biz.entity.MtProductEntity;
import com.spdd.mt.biz.entity.MtRechargeOrderEntity;
import com.spdd.mt.biz.entity.MtRechargeOrderPreEntity;
import com.spdd.mt.biz.exception.BusinessException;
import com.spdd.mt.biz.manager.feishu.FeiShuService;
import com.spdd.mt.biz.manager.model.request.*;
import com.spdd.mt.biz.manager.model.response.*;
import com.spdd.mt.biz.manager.service.NotifyService;
import com.spdd.mt.biz.service.MtProductService;
import com.spdd.mt.biz.service.MtRechargeOrderService;
import com.spdd.mt.biz.util.ProductUtils;
import com.spdd.mt.biz.util.StatusUtils;
import com.spdd.mt.common.http.HttpUtil;
import com.spdd.mt.common.http.exception.ConnectTimeoutException;
import com.spdd.mt.common.http.exception.ReadTimeoutException;
import com.spdd.mt.common.util.DateUtils;
import com.spdd.mt.common.util.IDGenerator;
import com.spdd.mt.common.util.JacksonUtils;
import com.spdd.mt.common.util.Md5Util;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.ParameterizedTypeReference;
import org.springframework.stereotype.Service;

import java.util.Date;
import java.util.Objects;
@Slf4j
@Service
public class SpddService {

    @Autowired
    private HttpUtil httpUtil;

    @Autowired
    private ConfigBean configBean;

    @Autowired
    private SpddMapping spddMapping;

    @Autowired
    private NotifyService notifyService;

    @Autowired
    private MtProductService mtProductService;

    @Autowired
    private MtRechargeOrderService rechargeOrderService;

    @Autowired
    private FeiShuService feiShuService;

    /**
     * SPDD 充值预下单服务
     *
     * @param order   预下单订单
     * @param product 预下单商品
     */
    public MtRechargeOrderPreEntity orderPreRecharge(MtRechargeOrderPreEntity order, MtProductEntity product) {
        log.info("SPDD预下单接口，merchNo={}, account={}, productCode={}", order.getMerchNo(), order.getAccount(), order.getProductCode());

        // 待更新实体
        MtRechargeOrderPreEntity updateEntity = null;

        SpddMapping.AppInfo appInfo = getAppInfo(order.getMerchNo());
        // 解析productCode为SPDD支持的格式，去掉分省编码
        String spddPackageId = ProductUtils.spddProductCode(order.getProductCode());
        String sign = Md5Util.createMD5ByStr(appInfo.getAppKey() + order.getAccount()
                + spddPackageId);

        SpddReqMakeContent content = SpddReqMakeContent.builder()
                .packageId(spddPackageId)
                .extOrder("")
                .phoneNo(order.getAccount())
                .operator(product.getOperator())
                .areaCode(product.getProvinceCode())
                .price(product.getPrice())
                .sign(sign)
                .build();

        // 构建SPDD请求对象
        SpddReq<SpddReqMakeContent> spddReq = buildRequest(order.getMerchNo(), content);

        String url = configBean.getHost() + "/mk/phone/add_minutes/try_order";
        log.info("调用SPDD预下单接口，参数为：{}", JacksonUtils.toJson(spddReq));

        try {
            SpddResBody<SpddOrderMakeRes> response = post(url, spddReq, new ParameterizedTypeReference<>() {
            });

            updateEntity = instanceEntity(updateEntity);

            SpddResResp resp = response.getResp();
            updateEntity.setSupplierReturnCode(resp.getCode());
            updateEntity.setSupplierReturnMessage(resp.getMsg());

            // SPDD 返回成功
            if (SpddStatus.SUCCESS_SERVER.getStatus().equals(resp.getCode())) {
                updateEntity.setReturnStatus(MtResponseStatus.MAKE_PRE_ORDER_SUCCESS.getValue());
                updateEntity.setReturnMessage(MtResponseStatus.MAKE_PRE_ORDER_SUCCESS.getDesc());
                updateEntity.setReturnCode("");
                return updateEntity;
            }

            // 处理失败
            updateEntity.setReturnStatus(MtResponseStatus.MAKE_PRE_ORDER_FAIL.getValue());
            SpddStatus spddStatus = SpddStatus.getByCode(resp.getCode(), SpddStatus.FAILURE);
            if (Objects.nonNull(spddStatus)) {
                updateEntity.setReturnMessage(spddStatus.getMessage());
                updateEntity.setReturnCode(spddStatus.getProblemType());
            }
        } catch (Exception e) {
            updateEntity = instanceEntity(updateEntity);

            updateEntity.setReturnStatus(MtResponseStatus.MAKE_PRE_ORDER_FAIL.getValue());
            updateEntity.setReturnMessage(MtResponseStatus.INNER_ERROR.getDesc());
            updateEntity.setReturnCode(generateReturnCode("o", "INNER"));
        }

        return updateEntity;
    }

    /**
     * SPDD 充值下单服务
     *
     * @param order   订单对象
     * @param product 商品对象
     */
    public MtRechargeOrderEntity orderRecharge(MtRechargeOrderEntity order, MtProductEntity product) {
        log.info("SPDD下单接口，mtOrderNo={}, orderNo={}", order.getMtOrderNo(), order.getOrderNo());

        // 待更新实体
        MtRechargeOrderEntity updateEntity = null;

        SpddMapping.AppInfo appInfo = getAppInfo(order.getMerchNo());
        // 解析productCode为SPDD支持的格式，去掉分省编码
        String spddPackageId = ProductUtils.spddProductCode(order.getProductCode());
        String sign = Md5Util.createMD5ByStr(appInfo.getAppKey() + order.getAccount()
                + spddPackageId + order.getMtOrderNo());

        SpddReqMakeContent content = SpddReqMakeContent.builder()
                .packageId(spddPackageId)
                .extOrder(order.getMtOrderNo())
                .phoneNo(order.getAccount())
                .operator(product.getOperator())
                .areaCode(product.getProvinceCode())
                .price(product.getPrice())
                .sign(sign)
                .build();

        // 构建SPDD请求对象
        SpddReq<SpddReqMakeContent> spddReq = buildRequest(order.getMerchNo(), content);
        String url = configBean.getHost() + "/mk/phone/add_minutes/new_order";
        log.info("调用SPDD下单接口，参数为：{}", JacksonUtils.toJson(spddReq));
        try {
            SpddResBody<SpddOrderMakeRes> response = post(url, spddReq, new ParameterizedTypeReference<>() {
            });

            SpddResResp resp = response.getResp();
            SpddOrderMakeRes spddOrder = response.getContent();

            // 返回码
            String respCode = resp.getCode();

            // 订单状态
            Integer orderStatus = StatusUtils.CHARGE_ORDER_STATUS_MAPPER.getOrDefault(respCode, OrderStatus.PROCESSING.getValue());

            // 下单成功，设置订单号和订单状态
            updateEntity = instanceEntity(updateEntity);
            if (Objects.nonNull(orderStatus)) {
                updateEntity.setSupplierOrderNo(spddOrder.getOrderId());
            }

            // 判断是不是重发订单,是则更新重发状态
            if (ReorderStatus.WAIT.getValue().equals(order.getReorderStatus())) {
                updateEntity.setReorderStatus(ReorderStatus.SUCCESS.getValue());
            }

            // 记录供应商返回的编码和信息
            updateEntity.setSupplierReturnCode(respCode);
            updateEntity.setSupplierReturnMessage(resp.getMsg());

            // 根据返回码映射订单状态
            updateEntity.setOrderStatus(orderStatus);
            // updateEntity.setReturnCode(respCode);

            // 记录订单的返回信息
            SpddStatus spddStatus = SpddStatus.getByCode(respCode);
            updateEntity.setReturnMessage(Objects.isNull(spddStatus) ? "供应商失败" : spddStatus.getMessage());
            // updateEntity.setReturnCode(Objects.isNull(spddStatus) ? "Other.Inner" : spddStatus.getProblemType());

            if (OrderStatus.FAILED.getValue().equals(updateEntity.getOrderStatus())) {
                updateEntity.setReturnStatus(MtResponseStatus.MAKE_PRE_ORDER_FAIL.getValue());
            } else {
                updateEntity.setReturnStatus(MtResponseStatus.MAKE_ORDER_SUCCESS.getValue());
            }
        }
        // catch (BusinessException be) {
        //     BusinessException 只有数据库更新失败才抛出
        //     log.info("SPDD下单接口，业务异常");
        //     log.error("SPDD下单接口，业务异常", be);
        //     throw be;
        // }
        catch (ConnectTimeoutException cte) {
            log.error("SPDD下单接口，http连接超时,按请求失败处理", cte);
            updateEntity = instanceEntity(updateEntity);
            updateEntity.setOrderStatus(OrderStatus.FAILED.getValue());
            updateEntity.setReturnMessage("供应商失败");
            updateEntity.setReturnStatus(MtResponseStatus.MAKE_PRE_ORDER_FAIL.getValue());
        } catch (ReadTimeoutException re) {
            log.error("SPDD下单接口，读超时，按请求成功处理", re);
            // 受理超时按进行中处理，通过 getOrder 进行判断下游是否下单成功，如果不成功会进行重发
            updateEntity = instanceEntity(updateEntity);
            updateEntity.setOrderStatus(OrderStatus.PROCESSING.getValue());
            updateEntity.setReturnStatus(MtResponseStatus.MAKE_ORDER_SUCCESS.getValue());
        } catch (Exception e) {
            // 非读超时的其他异常，标记为需要重发。其中lastReorderTime赋值为null，在sql中根据lastReorderTime是否为空判断是否更新重发次数和重发时间
            // 系统轮询 重发状态为 WAIT 的记录，进行重发操作
            log.error("SPDD下单接口，系统异常，需要重新发送", e);
            updateEntity = instanceEntity(updateEntity);
            updateEntity.setOrderStatus(OrderStatus.PROCESSING.getValue());
            updateEntity.setReorderStatus(ReorderStatus.WAIT.getValue());
            updateEntity.setReturnStatus(MtResponseStatus.MAKE_ORDER_SUCCESS.getValue());
        }

        // 订单终态时赋值完成时间
        if (Objects.nonNull(updateEntity) && OrderStatus.isFinal(updateEntity.getOrderStatus())) {
            updateEntity.setFinishTime(DateUtil.date());
        }

        return updateEntity;
    }

    /**
     * SPDD 充值查询服务
     *
     * @param order 订单实体
     */
    public MtRechargeOrderEntity orderQuery(MtRechargeOrderEntity order) {
        log.info("SPDD查询接口，mtOrderNo={}, orderNo={}", order.getMtOrderNo(), order.getOrderNo());

        MtRechargeOrderEntity updateEntity = null;

        SpddMapping.AppInfo appInfo = getAppInfo(order.getMerchNo());
        String sign = Md5Util.createMD5ByStr(appInfo.getAppKey() + order.getMtOrderNo());
        // String sign = Md5Util.createMD5ByStr(appInfo.getAppKey() + "111");
        SpddReqQueryContent content = SpddReqQueryContent.builder()
                .extOrderId(order.getMtOrderNo())
                // .extOrderId("111")
                .sign(sign)
                .build();

        // 构建SPDD请求对象
        SpddReq<SpddReqQueryContent> spddReq = buildRequest(order.getMerchNo(), content);
        String url = configBean.getHost() + "/go/phone/add_minutes/get_order";
        log.info("调用SPDD查询接口，参数为：{}", JacksonUtils.toJson(spddReq));

        try {
            SpddResBody<SpddOrderQueryRes> response = post(url, spddReq, new ParameterizedTypeReference<>() {
            });

            SpddResResp resp = response.getResp();
            SpddOrderQueryRes spddOrder = response.getContent();

            // 订单不存在，标记重发状态为待重发
            if (SpddStatus.ORDER_NOT_EXISTS.getStatus().equals(resp.getCode())) {
                log.info("SPDD查询订单不存在，mtOrderNo={}", order.getMtOrderNo());
                updateEntity = instanceEntity(updateEntity);
                updateEntity.setOrderStatus(OrderStatus.PROCESSING.getValue());
                updateEntity.setReorderStatus(ReorderStatus.WAIT.getValue());

                return updateEntity;
            }

            // 其他状态，表示SPDD有系统异常，需要发送系统错误预警，同时给美团返回系统中的订单状态
            if (!SpddStatus.SUCCESS_SERVER.getStatus().equals(resp.getCode())) {
                log.info("SPDD查询订单，返回错误， mtOrderNo={}, 错误码={}", order.getMtOrderNo(), resp.getCode());
                feiShuService.sendExceptionMsg(new BusinessException(ResponseCode.BUSINESS_ERROR, "SPDD查询订单，返回错误，错误码: {}" + resp.getCode()));

                return updateEntity;
            }

            // 状态码为00，订单对象为空，也返回异常
            if (Objects.isNull(spddOrder)) {
                log.info("SPDD查询订单，订单为空， mtOrderNo={}, 错误码={}", order.getMtOrderNo(), resp.getCode());
                feiShuService.sendExceptionMsg(new BusinessException(ResponseCode.BUSINESS_ERROR, "SPDD查询订单，订单为空，错误码: {}" + resp.getCode()));

                return updateEntity;
            }

            // String orderStatus = respContent.getString("status");
            // response.setOrderStatus(QUERY_ORDER_STATUS_MAPPER.getOrDefault(orderStatus,
            //         OrderStatusEnum.PROCESSING.getCode()));

            // 其他情况，根据 status 状态判断，设置订单状态
            updateEntity = instanceEntity(updateEntity);
            updateEntity.setOrderStatus(StatusUtils.QUERY_ORDER_STATUS_MAPPER.getOrDefault(spddOrder.getOrderStatus(), OrderStatus.PROCESSING.getValue()));
            // 供应商结算金额，单位:毫(分 * 1000)
            updateEntity.setSupplierContractPrice(spddOrder.getPrice());

            // 订单终态时赋值完成时间
            if (OrderStatus.isFinal(updateEntity.getOrderStatus())) {
                String endTime = spddOrder.getEndTime();
                // String finishTime = DateUtil.convertDateFormat(addEndTime);
                Date finishTime = DateUtils.parseDateTime(endTime);
                if (Objects.isNull(finishTime)) {
                    log.info("SPDD查询接口，addEndTime转换失败，取当前时间");
                    // finishTime = DateUtil.getCurrentDateTime();
                    finishTime = DateUtil.date();
                }
                updateEntity.setFinishDate(DateUtils.formatPureDay(finishTime));
                updateEntity.setFinishTime(finishTime);
            }
            updateEntity.setSupplierOrderNo(spddOrder.getOrderId());
        } catch (Exception e) {
            // 查询异常，不对订单做处理
            log.error("SPDD查询接口，请求SPDD订单查询接口异常", e);
        }

        return updateEntity;
    }

    /**
     * SPDD 手机号码归属地查询
     *
     * @param merchNo 美团商户号
     * @param phoneNo 手机号
     */
    public SpddPhoneZoneRes phoneZoneQuery(String merchNo, String phoneNo) {
        log.info("SPDD手机号码归属地查询接口，phoneNo={}", phoneNo);

        SpddPhoneZoneQueryContent content = SpddPhoneZoneQueryContent.builder()
                .phoneNo(phoneNo)
                .build();
        // 构建SPDD请求对象
        SpddReq<SpddPhoneZoneQueryContent> spddReq = buildRequest(merchNo, content);
        String url = configBean.getHost() + "/mk/phone/add_minutes/phone_zone_info";
        log.info("调用SPDD手机号码归属地查询接口，参数为：phoneNo={}", phoneNo);

        try {
            SpddResBody<SpddPhoneZoneRes> response = post(url, spddReq, new ParameterizedTypeReference<>() {
            });

            return response.getContent();
        } catch (Exception e) {
            // 查询异常，不对订单做处理
            log.error("SPDD手机号码归属地查询接口，请求SPDD手机号码归属接口异常", e);
        }

        return null;
    }

    /**
     * SPDD 回调服务
     *
     * @param notifyContent 回调信息
     */
    public boolean dealNotify(SpddReqNotifyContent notifyContent) {
        try {
            MtRechargeOrderEntity updateEntity = null;

            // SPDD系统存储美团的订单号，所以根据美团订单号查询订单
            String mtOrderNo = notifyContent.getExtOrderId();

            MtRechargeOrderEntity order = rechargeOrderService.getByMtOrderNo(mtOrderNo);
            if (Objects.isNull(order)) {
                log.info("SPDD回调通知失败，找不到订单，mtOrderNo={}", mtOrderNo);
                feiShuService.sendExceptionMsg(new BusinessException(ResponseCode.BUSINESS_ERROR, "SPDD回调通知失败，找不到订单，mtOrderNo=" + mtOrderNo));
                return false;
            }

            Integer spddOrderStatus = StatusUtils.NOTIFY_ORDER_STATUS_MAPPER.getOrDefault(notifyContent.getCode(), -1);
            log.info("SPDD回调，回调报文中映射后订单状态为:{}", spddOrderStatus);

            // 如果spddOrderStatus返回-1,说明状态不正确，不做任何操作
            if (spddOrderStatus.equals(-1)) {
                log.info("SPDD回调通知失败，状态映射异常，mtOrderNo={}", mtOrderNo);
                feiShuService.sendExceptionMsg(new BusinessException(ResponseCode.BUSINESS_ERROR, "SPDD回调通知失败，状态映射异常，mtOrderNo=" + mtOrderNo));
                return true;
            }

            // 如果通知订单状态非终态，也就是非成功、或失败，则不进行处理
            if (!OrderStatus.isFinal(spddOrderStatus)) {
                log.info("SPDD回调，非终态，不处理，mtOrderNo={}, code={} ", notifyContent.getOrderId(), notifyContent.getCode());
                return true;
            }

            // 本地成功，SPDD 为失败，则更新为返销
            boolean reExchange = OrderStatus.SUCCESS.getValue().equals(order.getOrderStatus())
                    && OrderStatus.FAILED.getValue().equals(spddOrderStatus);
            // 非返销的场景时本地已经终态则不处理通知
            if (OrderStatus.isFinal(order.getOrderStatus()) && !reExchange) {
                log.info("SPDD回调通知，订单 mtOrderNo={} 已为终态，且不满足返销，直接返回", mtOrderNo);
                return true;
            }
            log.info("SPDD回调，订单 orderNo={} 非终态，或者本地成功但通道失败处理为返销，开始更新订单", mtOrderNo);

            updateEntity = instanceEntity(updateEntity);
            updateEntity.setOrderStatus(spddOrderStatus);
            updateEntity.setNotifyReturnCode(notifyContent.getCode());
            updateEntity.setNotifyReturnMessage(notifyContent.getStatus());

            if (reExchange) {
                updateEntity.setOrderStatus(OrderStatus.REEXCHANGE.getValue());
            }

            // 重新设置订单号
            String spddOrderId = notifyContent.getOrderId();
            if (!order.getSupplierOrderNo().equals(spddOrderId)) {
                updateEntity.setSupplierOrderNo(spddOrderId);
            }

            // 供应商结算金额，单位:毫(分*1000)
            String price = notifyContent.getPrice();
            updateEntity.setSupplierContractPrice(price);

            // 终态时间
            String addEndTime = notifyContent.getEndTime();
            // String finishTime = DateUtil.convertDateFormat(addEndTime);
            Date finishTime = DateUtils.parseDateTime(addEndTime);
            if (Objects.isNull(finishTime)) {
                log.info("SPDD回调，addEndTime转换失败，取当前时间");
                // finishTime = DateUtil.getCurrentDateTime();
                finishTime = DateUtil.date();
            }
            updateEntity.setFinishTime(finishTime);
            updateEntity.setFinishDate(DateUtils.formatPureDay(finishTime));

            // 成功时赋值成功时间
            if (OrderStatus.SUCCESS.getValue().equals(updateEntity.getOrderStatus())) {
                updateEntity.setSuccessTime(finishTime);
            }

            // 更新订单
            rechargeOrderService.updateById(updateEntity, order.getTradeMonth(), order.getId());

            // 读取更新后的订单
            order = rechargeOrderService.getByMtOrderNo(order.getMtOrderNo());

            // 通知美团店铺，异步执行
            if (OrderStatus.isFinal(spddOrderStatus)) {
                notifyService.notifyMtOrder(order);
            }
            return true;
        } catch (Exception e) {
            log.error("处理回调通知异常", e);
            return false;
        }

    }

    private SpddMapping.AppInfo getAppInfo(String merchNo) {
        return spddMapping.getMapping().get(merchNo);
    }

    /**
     * 构建请求对象
     *
     * @param merchNo 美团商户号
     * @param content 请求内容
     */
    private <T> SpddReq<T> buildRequest(String merchNo, T content) {
        SpddMapping.AppInfo appInfo = getAppInfo(merchNo);
        String seqNo = IDGenerator.getSpddSeqNo();
        String timestamp = DateUtils.getSpddTimestamp();

        SpddReqHeader header = SpddReqHeader.builder()
                .seqNo(seqNo)
                .appId(appInfo.getAppId())
                .secretKey(Md5Util.createMD5ByStr(timestamp + seqNo + appInfo.getAppId() + appInfo.getAppKey()))
                .timestamp(timestamp)
                .version("1.0")
                .build();

        SpddReqBody<T> body = SpddReqBody.<T>builder()
                .content(content)
                .build();

        return SpddReq.<T>builder()
                .reqHeader(header)
                .reqBody(body)
                .build();
    }

    private MtRechargeOrderPreEntity instanceEntity(MtRechargeOrderPreEntity entity) {
        return Objects.isNull(entity) ? new MtRechargeOrderPreEntity() : entity;
    }

    private MtRechargeOrderEntity instanceEntity(MtRechargeOrderEntity entity) {
        return Objects.isNull(entity) ? new MtRechargeOrderEntity() : entity;
    }

    private String generateReturnCode(String type, String suffix) {
        String accountInvalid = "AccountInvalid.";
        String productInvalid = "ProductInvalid.";
        String other = "Other.";
        return switch (type) {
            case "a" -> accountInvalid + suffix;
            case "p" -> productInvalid + suffix;
            default -> other + suffix;
        };
    }

    protected <T, B> SpddResBody<T> post(String url, B body, ParameterizedTypeReference<SpddRes<T>> reference) {

        SpddRes<T> spddRes = httpUtil.post(url, body, reference);

        return spddRes.getBody();
    }
}
